home *** CD-ROM | disk | FTP | other *** search
/ Total Network Tools 2002 / NextStepPublishing-TotalNetworkTools2002-Win95.iso / Archive / Misc Servers / Zope.exe / TRANSACTION.PY < prev    next >
Encoding:
Python Source  |  2000-07-26  |  13.2 KB  |  370 lines

  1. ##############################################################################
  2. # Zope Public License (ZPL) Version 1.0
  3. # -------------------------------------
  4. # Copyright (c) Digital Creations.  All rights reserved.
  5. # This license has been certified as Open Source(tm).
  6. # Redistribution and use in source and binary forms, with or without
  7. # modification, are permitted provided that the following conditions are
  8. # met:
  9. # 1. Redistributions in source code must retain the above copyright
  10. #    notice, this list of conditions, and the following disclaimer.
  11. # 2. Redistributions in binary form must reproduce the above copyright
  12. #    notice, this list of conditions, and the following disclaimer in
  13. #    the documentation and/or other materials provided with the
  14. #    distribution.
  15. # 3. Digital Creations requests that attribution be given to Zope
  16. #    in any manner possible. Zope includes a "Powered by Zope"
  17. #    button that is installed by default. While it is not a license
  18. #    violation to remove this button, it is requested that the
  19. #    attribution remain. A significant investment has been put
  20. #    into Zope, and this effort will continue if the Zope community
  21. #    continues to grow. This is one way to assure that growth.
  22. # 4. All advertising materials and documentation mentioning
  23. #    features derived from or use of this software must display
  24. #    the following acknowledgement:
  25. #      "This product includes software developed by Digital Creations
  26. #      for use in the Z Object Publishing Environment
  27. #      (http://www.zope.org/)."
  28. #    In the event that the product being advertised includes an
  29. #    intact Zope distribution (with copyright and license included)
  30. #    then this clause is waived.
  31. # 5. Names associated with Zope or Digital Creations must not be used to
  32. #    endorse or promote products derived from this software without
  33. #    prior written permission from Digital Creations.
  34. # 6. Modified redistributions of any form whatsoever must retain
  35. #    the following acknowledgment:
  36. #      "This product includes software developed by Digital Creations
  37. #      for use in the Z Object Publishing Environment
  38. #      (http://www.zope.org/)."
  39. #    Intact (re-)distributions of any official Zope release do not
  40. #    require an external acknowledgement.
  41. # 7. Modifications are encouraged but must be packaged separately as
  42. #    patches to official Zope releases.  Distributions that do not
  43. #    clearly separate the patches from the original work must be clearly
  44. #    labeled as unofficial distributions.  Modifications which do not
  45. #    carry the name Zope may be packaged in any form, as long as they
  46. #    conform to all of the clauses above.
  47. # Disclaimer
  48. #   THIS SOFTWARE IS PROVIDED BY DIGITAL CREATIONS ``AS IS'' AND ANY
  49. #   EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  50. #   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  51. #   PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL DIGITAL CREATIONS OR ITS
  52. #   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  53. #   SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  54. #   LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  55. #   USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  56. #   ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  57. #   OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  58. #   OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  59. #   SUCH DAMAGE.
  60. # This software consists of contributions made by Digital Creations and
  61. # many individuals on behalf of Digital Creations.  Specific
  62. # attributions are listed in the accompanying credits file.
  63. ##############################################################################
  64. """Transaction management
  65.  
  66. $Id: Transaction.py,v 1.23.4.1 2000/07/26 17:11:04 klm Exp $"""
  67. __version__='$Revision: 1.23.4.1 $'[11:-2]
  68.  
  69. import time, sys, struct, POSException
  70. from struct import pack
  71. from string import split, strip, join
  72. from zLOG import LOG, ERROR, PANIC
  73. from POSException import ConflictError
  74.  
  75. # Flag indicating whether certain errors have occurred.
  76. hosed=0
  77.  
  78. class Transaction:
  79.     'Simple transaction objects for single-threaded applications.'
  80.     user=''
  81.     description=''
  82.     _connections=None
  83.     _extension=None
  84.     _sub=None # This is a subtrasaction flag
  85.  
  86.     def __init__(self, id=None):
  87.         self._id=id
  88.         self._objects=[]
  89.         self._append=self._objects.append
  90.  
  91.     def _init(self):
  92.         self._objects=[]
  93.         self._append=self._objects.append
  94.         self.user=self.description=''
  95.         if self._connections:
  96.             for c in self._connections.values(): c.close()
  97.             del self._connections
  98.  
  99.     def sub(self):
  100.         # Create a manually managed subtransaction for internal use
  101.         r=self.__class__()
  102.         r.user=self.user
  103.         r.description=self.description
  104.         r._extention=self._extension
  105.         return r
  106.         
  107.     def __str__(self): return "%.3f\t%s" % (self._id, self.user)
  108.  
  109.     def __del__(self):
  110.         if self._objects: self.abort(freeme=0)
  111.  
  112.     def abort(self, subtransaction=0, freeme=1):
  113.         '''Abort the transaction.
  114.  
  115.         This is called from the application.  This means that we haven\'t
  116.         entered two-phase commit yet, so no tpc_ messages are sent.
  117.         '''
  118.         t=v=tb=None
  119.         subj=self._sub
  120.         subjars=()
  121.  
  122.         if not subtransaction:
  123.             if subj is not None:
  124.                 # Abort of top-level transaction after commiting
  125.                 # subtransactions.
  126.                 subjars=subj.values()
  127.                 self._sub=None
  128.  
  129.         try:
  130.             # Abort the objects
  131.             for o in self._objects:
  132.                 try:
  133.                     j=getattr(o, '_p_jar', o)
  134.                     if j is not None: j.abort(o, self)
  135.                 except:
  136.                     if t is None:
  137.                         t,v,tb=sys.exc_info()
  138.  
  139.             # Ugh, we need to abort work done in sub-transactions.
  140.             while subjars:
  141.                 j=subjars.pop()
  142.                 j.abort_sub(self) # This should never fail
  143.         
  144.             if t is not None: raise t,v,tb
  145.  
  146.         finally:
  147.             tb=None
  148.             del self._objects[:] # Clear registered
  149.             if not subtransaction and freeme:
  150.                 if self._id is not None: free_transaction()
  151.             else: self._init()
  152.  
  153.     def begin(self, info=None, subtransaction=None):
  154.         '''Begin a new transaction.
  155.  
  156.         This aborts any transaction in progres.
  157.         '''
  158.         if self._objects: self.abort(subtransaction, 0)
  159.         if info:
  160.             info=split(info,'\t')
  161.             self.user=strip(info[0])
  162.             self.description=strip(join(info[1:],'\t'))
  163.  
  164.     def commit(self, subtransaction=None):
  165.         'Finalize the transaction'
  166.  
  167.         objects=self._objects
  168.         jars={}
  169.         subj=self._sub
  170.         subjars=()
  171.         if subtransaction:
  172.             if subj is None: self._sub=subj={}
  173.         else:
  174.             if subj is not None:
  175.                 if objects:
  176.                     # Do an implicit sub-transaction commit:
  177.                     self.commit(1)
  178.                     objects=[]
  179.                 subjars=subj.values()
  180.                 self._sub=None
  181.  
  182.         t=v=tb=None
  183.  
  184.         if (objects or subjars) and hosed:
  185.             # Something really bad happened and we don't
  186.             # trust the system state.
  187.             raise POSException.TransactionError, (
  188.                 
  189.                 """A serious error, which was probably a system error,
  190.                 occurred in a previous database transaction.  This
  191.                 application may be in an invalid state and must be
  192.                 restarted before database updates can be allowed.
  193.  
  194.                 Beware though that if the error was due to a serious
  195.                 system problem, such as a disk full condition, then
  196.                 the application may not come up until you deal with
  197.                 the system problem.  See your application log for
  198.                 information on the error that lead to this problem.
  199.                 """)
  200.  
  201.         try:
  202.  
  203.             # It's important that:
  204.             #
  205.             # - Every object in self._objects is either committed
  206.             #   or aborted.
  207.             #
  208.             # - For each object that is committed
  209.             #   we call tpc_begin on it's jar at least once
  210.             #
  211.             # - For every jar for which we've called tpc_begin on,
  212.             #   we either call tpc_abort or tpc_finish. It is OK
  213.             #   to call these multiple times, as the storage is
  214.             #   required to ignore these calls if tpc_begin has not
  215.             #   been called.
  216.             
  217.             ncommitted=0
  218.             try:
  219.                 for o in objects:
  220.                     j=getattr(o, '_p_jar', o)
  221.                     if j is not None:
  222.                         i=id(j)
  223.                         if not jars.has_key(i):
  224.                             jars[i]=j
  225.                             if subtransaction:
  226.                                 subj[i]=j
  227.                                 j.tpc_begin(self, subtransaction)
  228.                             else:
  229.                                 j.tpc_begin(self)
  230.                         j.commit(o,self)
  231.                     ncommitted=ncommitted+1
  232.  
  233.                 # Commit work done in subtransactions
  234.                 while subjars:
  235.                     j=subjars.pop()
  236.                     i=id(j)
  237.                     if not jars.has_key(i):
  238.                         jars[i]=j
  239.                     
  240.                     j.commit_sub(self)
  241.  
  242.                 for jar in jars.values():
  243.                     if not subtransaction:
  244.                         try: jar=jar.tpc_vote
  245.                         except: pass
  246.                         else: jar(self) # last chance to bail
  247.                 
  248.             except:
  249.                 t,v,tb=sys.exc_info()
  250.  
  251.                 # Ugh, we got an got an error during commit, so we
  252.                 # have to clean up.
  253.  
  254.                 # First, we have to abort any uncommitted objects.
  255.                 for o in objects[ncommitted:]:
  256.                     try:
  257.                         j=getattr(o, '_p_jar', o)
  258.                         if j is not None: j.abort(o, self)
  259.                     except: pass
  260.  
  261.                 # Then, we unwind TPC for the jars that began it.
  262.                 for j in jars.values():
  263.                     try: j.tpc_abort(self) # This should never fail
  264.                     except: pass
  265.  
  266.                 # Ugh, we need to abort work done in sub-transactions.
  267.                 while subjars:
  268.                     j=subjars.pop()
  269.                     j.abort_sub(self) # This should never fail
  270.  
  271.                 raise t,v,tb
  272.  
  273.             for j in jars.values():
  274.                 try:
  275.                     j.tpc_finish(self) # This should never fail
  276.                 except:
  277.                     # Bug if it does, we need to keep track of it and
  278.                     # not allow any more work without at least a restart!
  279.                     global hosed
  280.                     hosed=1
  281.                     LOG('ZODB', PANIC,
  282.                         "A storage error occurred in the last phase of a "
  283.                         "two-phase commit.  This shouldn\'t happen. "
  284.                         "The application may be in a hosed state, so "
  285.                         "transactions will not be allowed to commit "
  286.                         "until the site/storage is reset by a restart. ",
  287.                         error=sys.exc_info())
  288.                     if t is None:
  289.                         t,v,tb=sys.exc_info()
  290.                         
  291.             if t is not None: raise t,v,tb
  292.  
  293.         finally:
  294.             tb=None
  295.             del objects[:] # clear registered
  296.             if not subtransaction and self._id is not None: free_transaction()
  297.  
  298.     def register(self,object):
  299.         'Register the given object for transaction control.'
  300.         self._append(object)
  301.  
  302.     def note(self, text):
  303.         if self.description:
  304.             self.description = "%s\n\n%s" % (self.description, strip(text))
  305.         else: 
  306.             self.description = strip(text)
  307.     
  308.     def setUser(self, user_name, path='/'):
  309.         self.user="%s %s" % (path, user_name)
  310.  
  311.     def setExtendedInfo(self, name, value):
  312.         ext=self._extension
  313.         if ext is None:
  314.             ext=self._extension={}
  315.         ext[name]=value
  316.  
  317.  
  318. ############################################################################
  319. # install get_transaction:
  320.  
  321. try:
  322.     import thread
  323.  
  324. except:
  325.     _t=Transaction(None)
  326.     def get_transaction(_t=_t): return _t
  327.     def free_transaction(_t=_t): _t.__init__()
  328.  
  329. else:
  330.     _t={}
  331.     def get_transaction(_id=thread.get_ident, _t=_t, get=_t.get, None=None):
  332.         id=_id()
  333.         t=get(id, None)
  334.         if t is None: _t[id]=t=Transaction(id)
  335.         return t
  336.  
  337.     def free_transaction(_id=thread.get_ident, _t=_t):
  338.         id=_id()
  339.         try: del _t[id]
  340.         except KeyError: pass
  341.  
  342.     del thread
  343.  
  344. del _t
  345.  
  346. import __main__ 
  347. __main__.__builtins__.get_transaction=get_transaction
  348.     
  349.